/*
 *  HazardQueue.cpp
 *  pcm
 *
 *  Created by Yanick Candaux on 17.12.11.
 *  Copyright 2011 Heig-vd. All rights reserved.
 *
 */

#include "HazardQueue.h"
#include "def.hpp"
#include <cstring>


namespace pcm {
	
	bool HazardQueue::initializeQueue(){
		
		Node *node = new Node();
		node->setNext(NULL);
		Head = node;
		Tail = node;
		initializeHazardPointers();
		return true;
	}
	
	void HazardQueue::initializeHazardPointers(void){
		int tid, j;
		for (tid = 0; tid < NUMTHREADS; tid +=1){
			for (j=0; j<2 ; j +=1) {
				HP[tid][j] = NULL;
				LocalQueues[tid][j] = NULL;
			}
		}
	}
	
	
	
	bool HazardQueue::enq(BoardInfos *value, unsigned threadID){
		Node *last, *next;
		Node *node = getFreeNode(threadID);
		if (node == NULL) {
			return false;
		}
		node->setValue(value);
		node->setNext(NULL);
		while (true) {
			last = Tail;
			HP[threadID][0] = last;
			if (last == Tail) {
				next = last->next();
				if (last == Tail) {
					if (next == NULL) {
						if (bcasptr(last->pointerNext(),next,node)) {
							break;
						}
					
					}else {
						bcasptr(&Tail,last,next);
					
					}
				
				}
			}
			
		}
		bcasptr(&Tail,last,node);
		HP[threadID][0] = NULL;
		return true;
		
	}
	
	
	
	
	BoardInfos* HazardQueue::deq(unsigned threadID){
		Node *first, *last, *next;
		BoardInfos *value;
		while (true) {
			first = Head;
			HP[threadID][0] = first;
			if(first == Head){
				last = Tail;
				next = first->next();
				HP[threadID][1] = next;
				if (first == Head) {
					if (first == last) {
						if (next == NULL) {
							HP[threadID][0] = NULL;
							return NULL;		
						}
					bcasptr(&Tail, last,next);
					}else {
						value = next->value();
						if (bcasptr(&Head,first,next)) {
							HP[threadID][0] = NULL;
							HP[threadID][1] = NULL;
							freeNode(first, threadID);
							return value;
						}
					}
				
				}
			}
		}
	}
	
	
	void HazardQueue::freeNode(Node *node,unsigned tid){
		
		node->setNext(NULL);
		if (LocalQueues[tid][0] == NULL) {
			LocalQueues[tid][0] = node;
			LocalQueues[tid][1] = node;
		}else {
			LocalQueues[tid][1]->setNext(node);
			LocalQueues[tid][1] = node;
		}	
	}
	
	
	
	
	Node *HazardQueue::getFreeNode(unsigned tid){
		Node *node, *pred;
		pred = LocalQueues[tid][0];
		if (pred != NULL) {
			if (!isReferenced(pred)) {
				LocalQueues[tid][0] = pred->next();
				return pred;
			}
			while ((node = pred->next()) != NULL) {
				if (!isReferenced(node)) {
					pred->setNext(node->next());
					if ( pred->next() == NULL) {
						LocalQueues[tid][1] = pred;	
					}
					return node;
				}else {
					pred = node;
				}

			}
		}
		Node *node1= new Node();
		return (Node *) node1;	
	}
	
	
	
	
	bool HazardQueue::isReferenced(Node *node){
		int tid, i;
		for (tid=0;  tid < NUMTHREADS; tid += 1) {
			for (i=0; i< 2; i+= 1) {
				if (HP[tid][i] == node) {
					return true;
				}
			}
		}
		
		return false;
	}
	
	
	
	
}/* namespace pcm */
